package com.fxu.framework.core.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import com.fxu.framework.core.service.impl.ApiServiceImpl;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Diff[不同JavaBean]工具类
 * <p>把一个拥有对属性进行set和get方法的类，我们就可以称之为JavaBean。</p>
 * @author fangxu
 * @version 1.0.0
 */
public class DiffUtil {

	/**
	 * 获得新旧列表不同[添加，删除，更新]
	 * @param newList 新列表
	 * @param oldList 旧列表
	 * @param <T> 范型类别
	 * @return 获得不同对象
	 */
	public static <T> List<T> getDiff(List<T> newList, List<T> oldList) {
		if (CollUtil.isEmpty(newList)) {
			return new ArrayList<>();
		}
		String idName = FieldUtil.getPriKeyName(newList.get(0).getClass());
		return newList.stream().filter(newBean -> isDiff(newBean,
				CollUtil.findOne(oldList, old -> isSame(newBean, old, idName)))).collect(Collectors.toList());
	}

	/**
	 * JavaBean对象是否在JavaBean对象的List中
	 * @param newBean 新对象
	 * @param oldBean 旧对象
	 * @param <T> 范型类别
	 * @return 在true，不在false
	 */
	private static <T> boolean isDiff(T newBean, T oldBean) {
		if (oldBean == null || newBean == null) {
			return true;
		}
		String fieldName;
		for (Field field : ReflectUtil.getFields(oldBean.getClass())) {
			fieldName = field.getName();
			if (!ApiServiceImpl.SPECIAL_COLUMNS.contains(fieldName) && !isSame(newBean, oldBean, fieldName)) {
				return true;
			}
		}
		return false;
	}

	private static <T> boolean isSame(T newBean, T oldBean, String fieldName) {
		Object val_1 = ReflectUtil.getFieldValue(oldBean, fieldName);
		Object val_2 = ReflectUtil.getFieldValue(newBean, fieldName);
		// equals中的参数有判断 && val_2 != null
		return (val_1 == null && val_2 == null) || (val_1 != null && val_1.equals(val_2));
	}

	/**
	 * 不同集合类
	 */
	public static class Diff<T> {
		public List<T> inserts;
		public List<T> updates;
		public List<T> deletes;
		public Diff() {
			super();
			this.inserts = new ArrayList<>();
			this.updates = new ArrayList<>();
		}
		public Diff(List<T> inserts, List<T> updates, List<T> deletes) {
			super();
			this.inserts = inserts;
			this.updates = updates;
			this.deletes = deletes;
		}
	}

	/**
	 * 获得新旧列表不同[添加，删除，更新]
	 * @param oldList 旧列表
	 * @param newList 新列表
	 * @param equals 是否等于
	 * @param <T> 范型类别
	 * @return 获得不同对象
	 */
	public static <T> Diff<T> getDiff(List<T> oldList, List<T> newList, boolean equals) {
		if ((oldList == null || oldList.size() <= 0) && (newList == null || newList.size() <= 0)) {
			return new Diff<>();
		}
		List<T> inserts = new ArrayList<>(newList == null ? 0 : newList.size());
		List<T> updates = new ArrayList<>(newList == null ? 0 : newList.size());
		List<T> deletes = new ArrayList<>(oldList == null ? 0 : oldList.size());
		if (oldList == null || oldList.size() <= 0) {
			inserts.addAll(newList);
		} else if (newList == null || newList.size() <= 0) {
			deletes.addAll(oldList);
		} else {
			List<T> newOldList = new ArrayList<>();
			for (T oldBean : oldList) {
				if (getInList(newList, oldBean) == null) {
					// 删除，返回id的值
					deletes.add(oldBean);
				} else {
					newOldList.add(oldBean);
				}
			}
			for (T newBean : newList) {
				T find = getInList(newOldList, newBean);
				if (find == null) {
					// 添加，必须把id设置0
					inserts.add(newBean);
				} else if (!equals || (equals && !find.equals(newBean))) {
					// 更新
					updates.add(newBean);
				}
			}
		}
		return new Diff<>(inserts, updates, deletes);
	}

	/**
	 * JavaBean对象是否在JavaBean对象的List中
	 * @param list JavaBean对象List
	 * @param bean JavaBean对象
	 * @return 在true，不在false
	 */
	private static <T> T getInList(List<T> list, T bean) {
		if (list == null || list.size() <= 0 || bean == null) {
			return null;
		}
		String idName = FieldUtil.getPriKeyName(bean.getClass());
		Object id_1, id_2;
		for (T data : list) {
			if (data instanceof Map) {
				id_1 = ((Map<?, ?>) data).get(idName);
			} else {
				id_1 = ReflectUtil.getFieldValue(data, idName);
			}
			if (bean instanceof Map) {
				id_2 = ((Map<?, ?>) bean).get(idName);
			} else {
				id_2 = ReflectUtil.getFieldValue(bean, idName);
			}
			if (id_1 != null && id_2 != null && String.valueOf(id_1).equals(String.valueOf(id_2))) {
				return data;
			}
		}
		return null;
	}
}